package no.uib.hplims.models;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;

import org.vaadin.appfoundation.authentication.SessionHandler;
import org.vaadin.appfoundation.persistence.facade.FacadeFactory;

import com.vaadin.data.Validator.InvalidValueException;

public class RuleFactory {

	public enum FLAG_TYPE {
		RED, YELLOW, GREEN
	}

	public enum RULE_TYPE {
		LENGTH, ALLOW, DISALLOW
	}

	public enum POSITION {
		N_TERMINAL, C_TERMINAL, SEQUENCE
	}

	public enum RULE_META {
		DEFAULT, CUSTOM
	}

	private Rule rule = null;

	private List<Rule> allRules = null;
	private ArrayList<Rule> activeRules = null;
	private ArrayList<Rule> inactiveRules = null;

	public RuleFactory() {
		allRules = FacadeFactory.getFacade().list(Rule.class);
		if (activeRules == null) {
			activeRules = new ArrayList<Rule>();
		}
		if (inactiveRules == null) {
			inactiveRules = new ArrayList<Rule>();
		}
		Iterator<Rule> it = allRules.iterator();
		while (it.hasNext()) {
			Rule r = it.next();
			if (r.getMeta() == RULE_META.DEFAULT) {
				activeRules.add(r);
			} else if (r.getMeta() == RULE_META.CUSTOM) {
				inactiveRules.add(r);
			}
		}
	}

	public ArrayList<Rule> getActiveRules() {
		return activeRules;
	}

	public ArrayList<Rule> getInactiveRules() {
		return inactiveRules;
	}

	public void activateRule(Rule r) {
		if (inactiveRules.contains(r)) {
			inactiveRules.remove(r);
			System.out.println("Removed " + r + " from INactive rules");
		}
		if (!activeRules.contains(r)){
			activeRules.add(r);
			System.out.println("Added " + r + " to active rules");
		}
	}

	public void deactivateRule(Rule r) {
		if (activeRules.contains(r)) {
			activeRules.remove(r);
			System.out.println("Removed " + r + " from active rules");
		}
		if (!inactiveRules.contains(r)){
			inactiveRules.add(r);
			System.out.println("Added " + r + " to INactive rules");
		}
	}

	public Rule createNewRule(FLAG_TYPE flagType, RULE_TYPE ruleType,
			POSITION position, RULE_META meta, String ruleName,
			String aminoAcid, int maxOccurences, int minLength, int maxLength)
			throws InvalidValueException {

		rule = new Rule();

		// Give the new rule a name.
		if (ruleName == null || ruleName.isEmpty()) {
			throw new InvalidValueException("Please give the rule a name");
		} else {
			rule.setRuleName(ruleName);
		}

		if (ruleType == RULE_TYPE.ALLOW) {
			// ALLOW needs to specify one amino acid to allow
			if (aminoAcid == null || aminoAcid.isEmpty()) {
				throw new InvalidValueException(
						"Please specify amino acid to allow");
			} else if (aminoAcid.length() > 1) {
				throw new InvalidValueException(
						"Please specify only one amino acid.");
			} else {
				rule.setAminoAcid(aminoAcid);
			}
			// User is allowing a specific AA in N- or C-terminal.
			// There is only room for one AA in terminal positions.
			if ((position == POSITION.N_TERMINAL)
					|| (position == POSITION.C_TERMINAL)) {
				rule.setOccurences(1);
			} else {
				// User is allowing a specific AA in entire sequence.
				// Will have to specify maximum occurences in entire sequence.
				if (maxOccurences == -1) {
					throw new InvalidValueException(
							"Please specify maximum number of allowed occurences of "
									+ aminoAcid + ".");
				} else {
					rule.setOccurences(maxOccurences);
				}
			}
		} else if (ruleType == RULE_TYPE.DISALLOW) {
			if (aminoAcid == null || aminoAcid.isEmpty()) {
				throw new InvalidValueException(
						"Please specify amino acid to disallow.");
			} else if (aminoAcid.length() > 1) {
				throw new InvalidValueException(
						"Please specify only one amino acid.");
			} else {
				rule.setAminoAcid(aminoAcid);
				rule.setOccurences(0);
			}
		} else if (ruleType == RULE_TYPE.LENGTH) {
			position = POSITION.SEQUENCE;
			if (minLength < 0 || maxLength < 0) {
				throw new InvalidValueException(
						"Both minimum and maximum lengths must be set.");
			} else if (minLength > maxLength) {
				throw new InvalidValueException(
						"Maximum length can not be less than minimum lenght");
			} else {
				rule.setMinLength(minLength);
				rule.setMaxLength(maxLength);
			}
		}

		// Set the FLAG_TYPE of the new rule
		if (flagType == null) {
			throw new InvalidValueException(
					"Please specify rule warning level.");
		} else {
			rule.setFlagType(flagType);
		}

		// Set the RULE_TYPE of the new rule
		if (ruleType == null) {
			throw new InvalidValueException("Please specify rule type.");
		} else {
			rule.setRuleType(ruleType);
		}

		// Set the POSITION of the new rule
		if (position == null) {
			throw new InvalidValueException("Please specify position.");
		} else {
			rule.setPosition(position);
		}

		// Set logged in user as creator of rule
		rule.setCreator(SessionHandler.get());
		// Specify whether the rule is a custom or a default rule
		rule.setMeta(meta);

		// Store new rule in database
		FacadeFactory.getFacade().store(rule);
		// Add rule to all rules list to skip fetch from database
		allRules.add(rule);
		// Add rule to inactive rules list
		inactiveRules.add(rule);

		// Return rule
		return rule;
	}
}
